/*
 * Copyright (c) 2002, 2009, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */

package com.sun.corba.se.impl.oa.poa;

import java.util.Set;
import java.util.HashSet;
import java.util.Collections;
import java.util.Iterator;
import java.util.Map;
import java.util.WeakHashMap;

import org.omg.CORBA.OBJECT_NOT_EXIST;
import org.omg.CORBA.TRANSIENT;

import org.omg.CORBA.ORBPackage.InvalidName;

import org.omg.PortableServer.Servant;
import org.omg.PortableServer.POA;
import org.omg.PortableServer.POAManager;

import com.sun.corba.se.spi.oa.ObjectAdapter;
import com.sun.corba.se.spi.oa.ObjectAdapterFactory;

import com.sun.corba.se.spi.ior.ObjectAdapterId;

import com.sun.corba.se.spi.orb.ORB;

import com.sun.corba.se.spi.orbutil.closure.Closure;
import com.sun.corba.se.spi.orbutil.closure.ClosureFactory;

import com.sun.corba.se.spi.protocol.PIHandler;

import com.sun.corba.se.spi.logging.CORBALogDomains;

import com.sun.corba.se.impl.logging.POASystemException;
import com.sun.corba.se.impl.logging.OMGSystemException;

import com.sun.corba.se.impl.orbutil.ORBConstants;

import com.sun.corba.se.impl.oa.poa.POAManagerImpl;

public class POAFactory implements ObjectAdapterFactory {

  // Maps servants to POAs for deactivating servants when unexportObject is called.
  // Maintained by POAs activate_object and deactivate_object.
  private Map exportedServantsToPOA = new WeakHashMap();

  private Set poaManagers;
  private int poaManagerId;
  private int poaId;
  private POAImpl rootPOA;
  private DelegateImpl delegateImpl;
  private ORB orb;
  private POASystemException wrapper;
  private OMGSystemException omgWrapper;
  private boolean isShuttingDown = false;

  public POASystemException getWrapper() {
    return wrapper;
  }

  /**
   * All object adapter factories must have a no-arg constructor.
   */
  public POAFactory() {
    poaManagers = Collections.synchronizedSet(new HashSet(4));
    poaManagerId = 0;
    poaId = 0;
    rootPOA = null;
    delegateImpl = null;
    orb = null;
  }

  public synchronized POA lookupPOA(Servant servant) {
    return (POA) exportedServantsToPOA.get(servant);
  }

  public synchronized void registerPOAForServant(POA poa, Servant servant) {
    exportedServantsToPOA.put(servant, poa);
  }

  public synchronized void unregisterPOAForServant(POA poa, Servant servant) {
    exportedServantsToPOA.remove(servant);
  }

// Implementation of ObjectAdapterFactory interface

  public void init(ORB orb) {
    this.orb = orb;
    wrapper = POASystemException.get(orb,
        CORBALogDomains.OA_LIFECYCLE);
    omgWrapper = OMGSystemException.get(orb,
        CORBALogDomains.OA_LIFECYCLE);
    delegateImpl = new DelegateImpl(orb, this);
    registerRootPOA();

    POACurrent poaCurrent = new POACurrent(orb);
    orb.getLocalResolver().register(ORBConstants.POA_CURRENT_NAME,
        ClosureFactory.makeConstant(poaCurrent));
  }

  public ObjectAdapter find(ObjectAdapterId oaid) {
    POA poa = null;
    try {
      boolean first = true;
      Iterator iter = oaid.iterator();
      poa = getRootPOA();
      while (iter.hasNext()) {
        String name = (String) (iter.next());

        if (first) {
          if (!name.equals(ORBConstants.ROOT_POA_NAME)) {
            throw wrapper.makeFactoryNotPoa(name);
          }
          first = false;
        } else {
          poa = poa.find_POA(name, true);
        }
      }
    } catch (org.omg.PortableServer.POAPackage.AdapterNonExistent ex) {
      throw omgWrapper.noObjectAdaptor(ex);
    } catch (OBJECT_NOT_EXIST ex) {
      throw ex;
    } catch (TRANSIENT ex) {
      throw ex;
    } catch (Exception ex) {
      throw wrapper.poaLookupError(ex);
    }

    if (poa == null) {
      throw wrapper.poaLookupError();
    }

    return (ObjectAdapter) poa;
  }

  public void shutdown(boolean waitForCompletion) {
    // It is important to copy the list of POAManagers first because
    // pm.deactivate removes itself from poaManagers!
    Iterator managers = null;
    synchronized (this) {
      isShuttingDown = true;
      managers = (new HashSet(poaManagers)).iterator();
    }

    while (managers.hasNext()) {
      try {
        ((POAManager) managers.next()).deactivate(true, waitForCompletion);
      } catch (org.omg.PortableServer.POAManagerPackage.AdapterInactive e) {
      }
    }
  }

// Special methods used to manipulate global POA related state

  public synchronized void removePoaManager(POAManager manager) {
    poaManagers.remove(manager);
  }

  public synchronized void addPoaManager(POAManager manager) {
    poaManagers.add(manager);
  }

  synchronized public int newPOAManagerId() {
    return poaManagerId++;
  }

  public void registerRootPOA() {
    // We delay the evaluation of makeRootPOA until
    // a call to resolve_initial_references( "RootPOA" ).
    // The Future guarantees that makeRootPOA is only called once.
    Closure rpClosure = new Closure() {
      public Object evaluate() {
        return POAImpl.makeRootPOA(orb);
      }
    };

    orb.getLocalResolver().register(ORBConstants.ROOT_POA_NAME,
        ClosureFactory.makeFuture(rpClosure));
  }


  public synchronized POA getRootPOA() {
    if (rootPOA == null) {
      // See if we are trying to getRootPOA while shutting down the ORB.
      if (isShuttingDown) {
        throw omgWrapper.noObjectAdaptor();
      }

      try {
        Object obj = orb.resolve_initial_references(
            ORBConstants.ROOT_POA_NAME);
        rootPOA = (POAImpl) obj;
      } catch (InvalidName inv) {
        throw wrapper.cantResolveRootPoa(inv);
      }
    }

    return rootPOA;
  }

  public org.omg.PortableServer.portable.Delegate getDelegateImpl() {
    return delegateImpl;
  }

  synchronized public int newPOAId() {
    return poaId++;
  }

  public ORB getORB() {
    return orb;
  }
}
